package com.sis.main.server.ass;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.helpers.DefaultHandler;
import com.sis.main.client.DataLogin;
import com.sis.main.client.DataSecurity;
import com.sis.main.client.DataStreamerInfo;
//import com.sis.main.server.tda.ParseResponse.CommonStreamingResponse;
//import com.sis.main.server.tda.ParseResponse.HeartBeat;
//import com.sis.main.server.tda.ParseResponse.SnapshotResponse;
import com.sis.main.client.DataTrade;
import com.sis.main.server.jdo.GoogleDatabaseService;
import com.sis.main.server.jdo.PersistenceFactory;


/**
 * Main class implementing methods to interact with Ameritrade API 
 * using an interaction sequence diagram from the
 * user web interface to this service, as follows:
 * Instance variable dataLogin in this.constructor is set by 
 * 		user web interface after obtaining user's userid and password.
 * getDataLogin() (login setup for all ameritrade api services)
 * getDataStreamerInfo() (streaming data setup for getDataLevelIQuotes() below)
 * getDataLevelIBackfill() (to update datastore since last login)
 * getDataLevelIStreamingQuotes() (begin dataoutputstream for Level 1 Quotes; can't
 * 		get this to work, so switched to getDataLevelIQuotesXMLSnapshot below)
 * getDataLevelIQuotesXMLSnapshots
 * @author Eric Atkinson, StrategicInternationalSystems.com
 *
 */
public class AmeritradeSecuritiesService {

	private DataLogin dataLogin;  
	//vars not fully implemented after this line
	private DataStreamerInfo dataStreamerInfo;
	private DataTrade dataTrade;
	private byte byteLastFieldDelimiter =(byte)0xFF;


	private class DataLoginHandler extends DefaultHandler {
		private boolean isSessionID = false;
		private boolean isResult = false;
		private boolean isAccountID = false;
		private boolean isCompany = false;
		private boolean isSegment = false;
		
		public void startElement(
				String namespaceURI, 
				String localName, 
				String qName, 
				org.xml.sax.Attributes atts) 
		{
			if ( qName == "result") {
				isResult = true;
			}
			if ( qName == "session-id" ) {
				isSessionID = true;	
			}
			if ( qName == "account-id") {
				isAccountID = true;
			}
			if (qName == "company") {
				isCompany = true;
			}
			if (qName == "segment") {
				isSegment = true;
			}
		}//end startElement()

		
		public void characters(char[] chars, int start, int length) {
			 if (isSessionID){
				 dataLogin.setSessionID(new String(chars, start, length ));
				 isSessionID = false; //reset for next login
			 }
			 if (isResult) { 
				 String stringResult = new String(chars, start, length );
				 if (stringResult.equals("OK")) {
					 dataLogin.setLoggedIn(true);
				 }else{
					 dataLogin.setLoggedIn(false);
				 }
				 isResult = false; //reset for next login
			 }
			 if (isAccountID) {
				 dataLogin.setAccountID(new String(chars, start, length ));
				 isAccountID = false; //reset for next account in login or next login
			 }
			 if (isCompany) {
				 dataLogin.setCompany(new String(chars, start, length ));
				 isCompany = false; //reset for next account.company
			 }
			 if (isSegment) {
				 dataLogin.setSegment(new String(chars, start, length ));
				 isSegment = false; //reset for next account.segment
			 }
		}// end characters()
		
	
	}//end class LoginHandler
	
	private class DataXMLSnapshotHandler extends DefaultHandler {
		private DataSecurity dataSecurity;
		private boolean isResult;
		private boolean isDescription;
		private boolean isBid;
		private boolean isAsk;
		private boolean isLast;
		private boolean isOpen;
		private boolean isHigh;
		private boolean isLow;
		private boolean isClose;
		private boolean isVolume;
		private boolean isChange;
		private boolean isChangePercent;
		
		public DataXMLSnapshotHandler(DataSecurity dataSecurity) {
			this.dataSecurity = dataSecurity;
		}
		
		public void startElement(
				String namespaceURI, 
				String localName, 
				String qName, 
				org.xml.sax.Attributes atts) 
		{
			if ( qName == "result") {
				isResult = true;
			}
			if ( qName == "description") {
				isDescription = true;
			}
			if ( qName == "bid") {
				isBid = true;
			}
			if ( qName == "ask") {
				isAsk = true;
			}
			if ( qName == "last") {
				isLast = true;
			}
			if ( qName == "open") {
				isOpen = true;
			}
			if ( qName == "high") {
				isHigh = true;
			}
			if ( qName == "low") {
				isLow = true;
			}
			if ( qName == "close") {
				isClose = true;
			}
			if ( qName == "volume") {
				isVolume = true;
			}
			if ( qName == "change") {
				isChange = true;
			}
			if ( qName == "change-percent") {
				isChangePercent = true;
			}
		}//end startElement()

		
		public void characters(char[] chars, int start, int length) {
			 if (isResult) { 
				 String stringResult = new String(chars, start, length );
				 if (stringResult.equals("OK")) {
					 //then continue parsing
				 }else{
					try {
						throw new Exception("XML Snapshot failed");
					} catch (Exception e) {
						e.printStackTrace();
					}
				 }
				 //reset for next snapshot (unecesssary for 1 symbol per call
				 //but will keep just in case we use this for multiple securities)
				 isResult = false; 
			 }
			 if (isDescription) {
				 dataSecurity.setDescription(new String(chars, start, length ));
				 isDescription = false; //reset
			 }
			 if (isBid) {
				 dataSecurity.setBid(new Float(new String(chars, start, length )));
				 isBid = false; //reset
			 }
			 if (isAsk) {
				 dataSecurity.setAsk(new Float(new String(chars, start, length )));
				 isAsk = false; //reset
			 }
			 if (isLast) {
				 dataSecurity.setLast(new Float(new String(chars, start, length )));
				 isLast = false; //reset
			 }
			 if (isOpen) {
				 dataSecurity.setOpen(new Float(new String(chars, start, length )));
				 isOpen = false; //reset
			 }
			 if (isHigh) {
				 dataSecurity.setHigh(new Float(new String(chars, start, length )));
				 isHigh = false; //reset
			 }
			 if (isLow) {
				 dataSecurity.setLow(new Float(new String(chars, start, length )));
				 isLow = false; //reset
			 }
			 if (isClose) {
				 dataSecurity.setClose(new Float(new String(chars, start, length )));
				 isClose = false; //reset
			 }
			 if (isVolume) {
				 dataSecurity.setVolume(new Long(new String(chars, start, length )));
				 isVolume = false; //reset
			 }
			 if (isChange) {
				 dataSecurity.setChange(new Float(new String(chars, start, length )));
				 isChange = false; //reset
			 }
			 if (isChangePercent) {
				 dataSecurity.setPercentChange(new Double(new String(chars, start, length - 1 )));
				 isChangePercent = false; //reset
			 }

		}// end characters()
		
	}//end class LoginHandler

	
	private class DataStreamerInfoHandler extends DefaultHandler {
		private boolean isResult = false;
		private boolean isStreamerURL = false;
		private boolean isToken = false;
		private boolean isTimestamp = false;
		private boolean isCdDomainID = false;
		private boolean isUserGroup = false;
		private boolean isAccessLevel = false;
		private boolean isAcl = false;
		private boolean isAppId = false;
		private boolean isAuthorized = false;
		
		public void startElement(
				String namespaceURI, 
				String localName, 
				String qName, 
				org.xml.sax.Attributes atts) 
		{
			if ( qName.equals("result") ) {
				isResult = true;
			}
			if ( qName.equals("streamer-url") ) {
				isStreamerURL = true;
			}
			if ( qName.equals("token") ) {
				isToken = true;
			}
			if ( qName.equals("timestamp") ) {
				isTimestamp = true;
			}
			if ( qName.equals("cd-domain-id") ) {
				isCdDomainID = true;
			}
			if ( qName.equals("usergroup") ) {
				isUserGroup  = true;
			}
			if ( qName.equals("access-level") ) {
				isAccessLevel  = true;
			}
			if ( qName.equals("acl") ) {
				isAcl   = true;
			}
			if ( qName.equals("app-id") ) {
				isAppId    = true;
			}
			if ( qName.equals("authorized") ) {
				isAuthorized     = true;
			}
		}//end startElement()

		
		public void characters(char[] chars, int start, int length) {
			 if (isResult) { 
				 String stringResult = new String(chars, start, length );
				 if (stringResult.equals("OK")) {
					 dataStreamerInfo.setIsStreamerInfo(true);
				 }else{
					 dataStreamerInfo.setIsStreamerInfo(false);
				 }
				 isResult = false; //reset for next streamer info
			 }
			 if (isStreamerURL) {
				 dataStreamerInfo.setStreamerURL(new String(chars, start, length ));
				 isStreamerURL = false; //reset
			 }
			 if (isToken) {
				 dataStreamerInfo.setToken(new String(chars, start, length ));
				 isToken = false; //reset
			 }
			 if (isTimestamp) {
				 dataStreamerInfo.setTimestamp(new String(chars, start, length ));
				 isTimestamp = false; //reset
			 }
			 if (isCdDomainID) {
				 dataStreamerInfo.setCdDomainID(new String(chars, start, length ));
				 isCdDomainID = false; //reset
			 }
			 if (isUserGroup) {
				 dataStreamerInfo.setUserGroup(new String(chars, start, length ));
				 isUserGroup = false; //reset
			 }
			 if (isAccessLevel) {
				 dataStreamerInfo.setAccessLevel(new String(chars, start, length ));
				 isAccessLevel = false; //reset
			 }
			 if (isAcl) {
				 dataStreamerInfo.setAcl(new String(chars, start, length ));
				 isAcl = false; //reset
			 }
			 if (isAppId) {
				 dataStreamerInfo.setAppID(new String(chars, start, length ));
				 isAppId = false; //reset
			 }
			 if (isAuthorized) {
				 dataStreamerInfo.setAuthorized(new String(chars, start, length ));
				 isAuthorized = false; //reset
			 }
		}// end characters()

	}//end class DataStreamerInfo

	
	private class DataInputStreamHandler {
		private DataInputStream datainputstream;
		private byte byteEndOfStreamDelimiter=(byte)0x0A;

		
		public DataInputStreamHandler( ByteArrayInputStream bytearrayinputstream) {
			//this.datainputstream = bytearrayinputstream;
			try {
				for(;;){
					Integer byteHeaderID = -1;
					int intStart = 0;
					int intEnd = 1;
					byte[] b = new byte[2];
					byteHeaderID = bytearrayinputstream.read(b, intStart, intEnd);
					String stringHeaderID = (bytearrayinputstream.toString()).substring(intStart, intEnd);
					/* Handle reply to use handler as indicated by HeaderID; however, 
					 * this app is requesting CommonStreamingResponse if there 
					 * are symbols in user's protfolio. Otherwise, streamer server
					 * returns only a heartbeat to let app know that connection 
					 * hasn't been terminated by Ameritrade.
					 */
					if(stringHeaderID=="H"){
						new HeartbeatHandler(); 
					}else if(stringHeaderID=="N"){
						new  SnapshotResponseHandler();	//not implemented yet	
					}else if(stringHeaderID=="S"){
						new CommonStreamingResponseHandler();
					}else{
						throw new Exception("Unknown HeaderID");
					}
				}//endfor
			} catch (Exception e) {
				e.printStackTrace();
			}
		}//end constructor

		
		private class CommonStreamingResponseHandler {
			
			
			public CommonStreamingResponseHandler() {
				 try {
					//Streaming ID Length
					short  Messagelength = datainputstream.readShort();
					//ID from Server IDs list identifying the response content
					short  SID = datainputstream.readShort(); 
					getServiceIDHandler (SID);
				} catch (IOException e) {
					e.printStackTrace();
				}
				 
			}//end constructor CommonStreamingResponseHandler
			
			private void getServiceIDHandler(short SID) {
				if(SID==1){       //      QUOTE
					new LevelIQuoteHandler();	
				}else if(SID==5){  //      TIMESALE
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==10){ //      RESPONSE
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==16){ //      AUTHORIZER
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==18){ //      OPTION
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==14){ //      ACTIVES_ISLAND
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==23){ //      ACTIVES_NYSE
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==24){ //      ACTIVES_AMEX
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==25){ //      ACTIVES_NASDAQ
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==26){ //      ACTIVES_OTCBB
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==27){ //      NEWS
					//new NEWS();
				}else if(SID==28){ //      NEWS_HISTORY
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==33){ //      OPTION_LOOKUP
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==35){ //      ACTIVES_OPTIONS
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==60){ //      ADAP_ISLAND
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==61){ //      ADAP_ECNS
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==62){ //      ADAP_NASDAQ
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==63){ //      ADAP_REDI
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==64){ //      ADAP_ARCA
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==65){ //      ADAP_BRUT
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==80){ //      AMTD_UPDATE
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==81){ //      NYSE_BOOK
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==82){ //      NYSE_CHART
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==83){ //      NASDAQ_CHART
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==85){ //      INDEX_CHART
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==84){ //      OPRA_BOOK
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==87){ //      TOTAL_VIEW
					throw new RuntimeException("NOT IMPLEMENTED");
				}else if(SID==100){ //      STREAMER_SERVER
					try {
						new StreamerServerResponseHandler();
					} catch (IOException e) {
						e.printStackTrace();
					}	
				}else{
					throw new RuntimeException("unknown recepient field >>"+SID+"<<");
				}
	
			}//end getSeriveIDHandler()
			
			private class LevelIQuoteHandler {
				
				public LevelIQuoteHandler () {
					try {
						//Symbol Column (or Column 0) is first ColumnType returned 
						byte ColumnType = datainputstream.readByte();
						short SymbolLength = datainputstream.readShort();
						String Symbol = new String(getBytes(SymbolLength));
						dataTrade = new DataTrade();
						dataTrade.setSymbol(Symbol);
						for(;;){  //Repeating Data
							ColumnType = datainputstream.readByte();	//requested column or end of stream segment delimiter
							if(ColumnType == byteLastFieldDelimiter){
								persist(dataTrade); 
								//...into datastore (for either insert or update)
								getConstant(byteEndOfStreamDelimiter);
								return;
							}else{
								setColumn(ColumnType);
							}	
						}//end for	
					} catch (IOException e) {
						e.printStackTrace();
					} 		
				}// end constructor LevelIQuoteHandler
				
				private boolean setColumn(int rc) throws IOException
				{
					if(rc==0){}//"SYMBOL" already read, so do nothing;
					else if(rc==1){dataTrade.setBid(datainputstream.readFloat()); return true;}
					else if(rc==2){dataTrade.setAsk(datainputstream.readFloat()); return true;}
					else if(rc==3){dataTrade.setLast(datainputstream.readFloat()); return true;}
					else if(rc==4){dataTrade.setBidSize(datainputstream.readInt());return true;}
					else if(rc==5){dataTrade.setAskSize(datainputstream.readInt()); return true;}
					else if(rc==6){dataTrade.setBidID(datainputstream.readChar());return true;}
					else if(rc==7){dataTrade.setAskID(datainputstream.readChar());return true;}
					else if(rc==8){dataTrade.setVolume(datainputstream.readLong());return true;}
					else if(rc==9){dataTrade.setLastSize(datainputstream.readInt());return true;}
					else if(rc==10){dataTrade.setTradetime(datainputstream.readInt());return true;}
					else if(rc==11){dataTrade.setQuoteTime(datainputstream.readInt());return true;}
					else if(rc==12){dataTrade.setHigh(datainputstream.readFloat());return true;}
					else if(rc==13){dataTrade.setLow(datainputstream.readFloat());return true;}
					else if(rc==14){dataTrade.setTick(datainputstream.readChar());return true;}
					else if(rc==15){dataTrade.setClose(datainputstream.readFloat());return true;}
					else if(rc==16){dataTrade.setExchange(datainputstream.readChar());return true;}
					else if(rc==17){dataTrade.setMarginable(datainputstream.readBoolean());return true;}
					else if(rc==18){dataTrade.setShortable(datainputstream.readBoolean());return true;}
					else if(rc==19){dataTrade.setIslandBid(datainputstream.readFloat());return true;}
					else if(rc==20){dataTrade.setIslandAsk(datainputstream.readFloat());return true;}
					else if(rc==21){dataTrade.setIslandVolume(datainputstream.readLong());return true;}
					else if(rc==22){dataTrade.setQuoteDate(datainputstream.readInt());return true;}
					else if(rc==23){dataTrade.setTradeDate(datainputstream.readInt());return true;}
					else if(rc==24){dataTrade.setVolatility(datainputstream.readFloat());return true;}
					else if(rc==25){dataTrade.setDescription(getString());return true;}
					else if(rc==26){dataTrade.setTradeID(datainputstream.readChar());return true;}
					else if(rc==27){dataTrade.setDigits(datainputstream.readInt());return true;}
					else if(rc==28){dataTrade.setOpen(datainputstream.readFloat());return true;}
					else if(rc==29){dataTrade.setChange(datainputstream.readFloat());return true;}
					else if(rc==30){dataTrade.set52WeekHigh(datainputstream.readFloat());return true;}
					else if(rc==31){dataTrade.set52WeekLow(datainputstream.readFloat());return true;}
					else if(rc==32){dataTrade.setPERatio(datainputstream.readFloat());return true;}
					else if(rc==33){dataTrade.setDividendAmount(datainputstream.readFloat());return true;}
					else if(rc==34){dataTrade.setDividendYield(datainputstream.readFloat());return true;}
					else if(rc==35){dataTrade.setIslandBidSize(datainputstream.readInt());return true;}
					else if(rc==36){dataTrade.setIslandAskSize(datainputstream.readInt());return true;}
					else if(rc==37){dataTrade.setNav(datainputstream.readFloat());return true;}
					else if(rc==38){dataTrade.setFund(datainputstream.readFloat());return true;}
					else if(rc==39){dataTrade.setExchangeName(getString());return true;}
					else if(rc==40){dataTrade.setDividendDate(getString());return true;}
					else{
						throw new RuntimeException("unknown "+rc);
					}
					return false;
				}//end getColumn()
				
				public void persist(DataTrade dataTrade) {
			        PersistenceManager persistencemanager = PersistenceFactory.get().getPersistenceManager();
			        try {
			        	persistencemanager.makePersistent(dataTrade);
			        } finally {
			        	persistencemanager.close();
			        }
			  }//end persist()

			}//end class LevelIQuoteHandler
			
			private class StreamerServerResponseHandler {
			
				StreamerServerResponseHandler() throws IOException {
					int MessageLength=datainputstream.readInt();
					short SID=datainputstream.readShort();
					byte Column1=datainputstream.readByte();
					short ServiceID=datainputstream.readShort();
					byte Column2=datainputstream.readByte();
					short Returncode=datainputstream.readShort();
					getReturnCode(Returncode);
					byte Column3=datainputstream.readByte();
					short DescriptionLength=datainputstream.readShort();
					String Description=getString(DescriptionLength);
					readDelimeter();
				}//end constructor StreamerServerResponse

				String getReturnCode(short rc) {
					if(rc==0){return "SUCCESS";}
					else if(rc==1){return "SERVICE_DOWN";}
					else if(rc==2){return "SERVICE_TIMEOUT";}
					else if(rc==3){return "LOGIN_DENIED";}
					else if(rc==4){return "AUTHORIZER_BUSY";}
					else if(rc==5){return "AUTHORIZER_DOWN";}
					else if(rc==6){return "USER_NOT_FOUND";}
					else if(rc==7){return "ACCOUNT_ON_HOLD";}
					else if(rc==8){return "ACCOUNT_FROZEN";}
					else if(rc==9){return "UNKNOWN_FAILURE";}
					else if(rc==10){return "FAILURE";}
					else if(rc==11){return "SERVICE_NOT_AVAILABLE";}
					else if(rc==12){return "CLOSE_APPLET";}
					else if(rc==13){return "USER_STATUS";}
					else if(rc==14){return "ACCOUNT_EMPTY";}
					else if(rc==15){return "MONOPOLIZE_ACK";}
					else if(rc==16){return "NOT_AUTHORIZED_FOR_SERVICE";}
					else if(rc==17){return "NOT_AUTHORIZED_FOR_QUOTE";}
					else if(rc==18){return "STREAMER_SERVER_ID";}
					else{
						throw new RuntimeException("unknown RC "+rc);
					}//endif
				}// end getReturnCode()
			
			}//end class StreamerServerReponse

		}//end class CommonStreamingResponseHandler

		private class SnapshotResponseHandler {

		}

		private class HeartbeatHandler {
					
			public HeartbeatHandler() {
				try {
					byte SubType = datainputstream.readByte();		
					if(SubType=='T'){ //T indicates next field is time
						//time in milliseconds from 00:00:00 UTC on January 1, 1970
						long Timestamp = datainputstream.readLong();
					}else if(SubType=='H'){ //H indicates no time value is given
						//so do nothing
					}else{
						throw new RuntimeException("unknown " + SubType);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
						
			}//end constructor HeartbeatHandler
			
		}	//end class HeartbeatHandler
		
		 void readDelimeter() throws IOException {
				 getConstant(byteLastFieldDelimiter);
				 getConstant(byteEndOfStreamDelimiter);
			}
		
		public byte[] getBytes(int len) throws IOException {
			byte[]bytes=new byte[len];
			int read=0;
			for(;;){	
				int i= datainputstream.read(bytes,read,len-read);
				if(i<=0){
					throw new RuntimeException("failed to read "+i+"  read was "+read);
				}
				read+=i;
				if(read==len){
					break;
				}
			}//enfor
		 	if(read!=len){
		 		throw new RuntimeException("didnt read "+len+" actually read "+read);
		 	}
		 	return bytes;
		}//end readBytes()
		
		void getConstant(int val) throws IOException
		{
			byte r1 = datainputstream.readByte();
			if(r1!=val){
				throw new RuntimeException("incorrect "+r1+" expected "+val);
			}
			
		}
		
		public String getString() throws IOException
		{
			short len=datainputstream.readShort();
			return getString(len);
		}
		
		public String getString(int len) throws IOException
		{
		return new String(getBytes(len));
		}

		public short convertByteArrayToShort (byte[] arr, int start) {
			int low = arr[start] & 0xff;
			int high = arr[start+1] & 0xff;
			return (short)( high << 8 | low );
		}
		
	
	}// end class DataInputStreamHandler

	public DataLogin getDataLogin(DataLogin dataLogin) {
		this.dataLogin = dataLogin;
		return getDataLogin();
	}
	

	public DataLogin getDataLogin() {
		if (dataLogin.isLoggedIn() == false){
			  URL url;
			  URLConnection urlconnection;
			  String stringURL;
			  try {
				//set get request
			    stringURL = "https://apis.tdameritrade.com/apps/100/LogIn?source=" 
					+ dataLogin.getSourceID() + "&version=" 
					+ dataLogin.getVersion();
				url = new URL (stringURL);
				
				//send get request
				urlconnection = url.openConnection();
				urlconnection.setDoInput (true); 
				urlconnection.setDoOutput (true); 
				urlconnection.setUseCaches (false); 
				urlconnection.setRequestProperty("Content-Type", 
						"application/x-www-form-urlencoded");
				
			  //set post request	  
			  OrderedHashMap orderedhashmap = new OrderedHashMap();
			  orderedhashmap.put("userid",dataLogin.getUserID());
			  orderedhashmap.put("password",dataLogin.getPassword());
			  orderedhashmap.put("source",dataLogin.getSourceID());  
			  orderedhashmap.put("version",dataLogin.getVersion());
			  StringBuffer stringbufferPost = new StringBuffer();
			  for(int i = 0; i < orderedhashmap.size(); i++){
				   String key=(String) orderedhashmap.getKey(i);	
				   String value=(String) orderedhashmap.getValue(i);

					stringbufferPost.append(URLEncoder.encode(key, "UTF-8") + 
							   "=" + URLEncoder.encode(value, "UTF-8"));
				   if( i < orderedhashmap.size() - 1){
					   stringbufferPost.append("&");
				   }
				}//end for
			    String 	stringPost = stringbufferPost.toString();
			    
			   //send post request
				DataOutputStream dataoutputstream = 
					new DataOutputStream (urlconnection.getOutputStream ());
				dataoutputstream.writeBytes (stringPost);
				dataoutputstream.flush ();
				dataoutputstream.close ();
			 	
				//get reply

				int intEndOfFile = -1;
				String stringReply = getByteArrayOutputStream(urlconnection.getInputStream(), 
						intEndOfFile);
				urlconnection.getInputStream().close();
				
				//parse reply and set datalogin variables using LoginHandler	
				SAXParserFactory saxparserfactory = SAXParserFactory.newInstance() ;
				SAXParser saxparser = saxparserfactory.newSAXParser(); 	
				saxparser.parse(new ByteArrayInputStream(stringReply.getBytes()),
						new DataLoginHandler());

				} catch (Exception e) {
					e.printStackTrace();
				}

		}
		return dataLogin;
	}


	public DataStreamerInfo getDataStreamerInfo() {
		if (dataStreamerInfo == null) {
			dataStreamerInfo = new DataStreamerInfo();
			String stringURL = "https://apis.tdameritrade.com/apps/100/" 
				+ "StreamerInfo?source=<" 
				+ getDataLogin().getSourceID() + ">";
			try {
				URL url = new URL(stringURL);
				int intEndOfFile = -1;
				String stringReply = getByteArrayOutputStream( url.openStream(), intEndOfFile );
				SAXParserFactory saxparserfactory = SAXParserFactory.newInstance() ;
				SAXParser saxparser = saxparserfactory.newSAXParser(); 	
				saxparser.parse(new ByteArrayInputStream(stringReply.getBytes()),
						new DataStreamerInfoHandler());
			} catch (Exception e) {
				e.printStackTrace();
			}	
		}
		return dataStreamerInfo;
	}
	
	public void getDataLevelIQuoteUsingXMLSnapshot(DataSecurity dataSecurity) {

    	//URL is https://apis.tdameritrade.com/apps/100/Quote?source=#sourceID#&symbol=#symbol#
    	String stringURLBase="https://apis.tdameritrade.com/apps/100/Quote";

	    	String stringURLForEncoding = "?source=" 
	    		+ getDataLogin().getSourceID() + "&symbol=" ;
	    	stringURLForEncoding = stringURLForEncoding 
	    		+ dataSecurity.getSymbol();
	    	try {
	    	String stringURL = stringURLBase + stringURLForEncoding;
	    	//took encoding out per Ameritrade API support (Medved)
	    	//stringURL = stringURLBase 
			//	+ URLEncoder.encode(stringURLForEncoding, "UTF-8");
	    	URL url = new URL(stringURL);
			int intEndOfFile = -1;
			String stringReply = getByteArrayOutputStream( url.openStream(), intEndOfFile );
			SAXParserFactory saxparserfactory = SAXParserFactory.newInstance() ;
			SAXParser saxparser = saxparserfactory.newSAXParser(); 	
			saxparser.parse(new ByteArrayInputStream(stringReply.getBytes()),
					new DataXMLSnapshotHandler(dataSecurity));

	    	} catch (Exception e) {
				e.printStackTrace();
			}

	}//getDataLevelIQuotesXMLSnapshot()
	
	public void getDataLevelIQuotesUsingStreamingSnapshot() {
			//get list of securities in watch list
		   	PersistenceManager persistencemanager = PersistenceFactory.get().getPersistenceManager();
	    	String query = "select from " + DataSecurity.class.getName();
	    	List<DataSecurity> dataSecurities =  (List<DataSecurity>) persistencemanager.newQuery(query).execute();	
	    	if (dataSecurities.size() == 0) {
	    		return;
	    	}
			
	    	String stringURLBase="http://" + getDataStreamerInfo().getStreamerURL() 
				+ "/!U=";
			
			String stringURLForEncoding = getDataLogin().getAccountID() +
			"&W=" + getDataStreamerInfo().getToken() +
			"&A=userid=" + getDataLogin().getAccountID() +
			"&token=" + getDataStreamerInfo().getToken() +
			"&company=" + getDataLogin().getCompany() +     
			"&segment="+ getDataLogin().getSegment() +  
			"&cddomain=" + getDataStreamerInfo().getCdDomainID() +
			"&usergroup=" + getDataStreamerInfo().getUserGroup() +
			"&accesslevel="+ getDataStreamerInfo().getAccessLevel() +
			"&authorized=" + getDataStreamerInfo().getAuthorized() +
			"&acl=" + getDataStreamerInfo().getAcl() +
			"&timestamp=" + getDataStreamerInfo().getTimestamp() +
			"&appid=" + getDataStreamerInfo().getAppID() 
			+"|S=QUOTE" 
			//+"&C=SUBS"
			+"&C=GET"
			+"&P="; //get SYMBOL
		    for (DataSecurity dataSecurity : dataSecurities) {
		    	stringURLForEncoding = stringURLForEncoding + dataSecurity.getSymbol();
		        if (dataSecurities.iterator().hasNext()) {
		        	stringURLForEncoding = stringURLForEncoding + "+";      
		        }//end if
		    }//end for 
			stringURLForEncoding = stringURLForEncoding +
			"&T=0+" + 	
			"1+" + 		//get BID
			"2+" + 		//get ASK 
			"3+" + 		//get LAST 
			"8+" +		//get VOLUME
			"9+" +		//get LASTSIZE 
			"10+" + 	//get TRADETIME
			//"11+" +		//get QUOTETIME 
			"12+" + 	//get HIGH
			"13+" + 	//get LOW
			"14+" +		//get TICK
			"15+" + 	//get CLOSE
			"16+" + 	//get EXCHANGE
			"17+" +		//get MARGINABLE 
			"18+" 		//get SHORTABLE
			;	

			try {
				String stringURL = stringURLBase 
					+ URLEncoder.encode(stringURLForEncoding, "UTF-8");				
			//new DataInputStreamHandler( new DataInputStream ( 
			//	new URL(stringURL).openStream()) ) ;
				@SuppressWarnings("unused")
				String stringReply = getByteArrayOutputStream( 
						new URL (stringURL).openStream(), byteLastFieldDelimiter );
				ByteArrayInputStream bytearrayinputstream = new ByteArrayInputStream(stringReply.getBytes());
				new DataInputStreamHandler(bytearrayinputstream);
			} catch (MalformedURLException e) {
					e.printStackTrace();
			} catch (IOException e) {
					e.printStackTrace();
			}

	}//end getLevelIQuotes
	
	private String getByteArrayOutputStream(InputStream inputstream, int intEndOfFile) {
		ByteArrayOutputStream bytearrayoutputstream = 
			new ByteArrayOutputStream();
		byte bytearrayBuffer[] = new byte[10000];
		int intBytesRead;
		int intStart = 0;
		try {
			while( (intBytesRead = inputstream.read(bytearrayBuffer)) 
					!= intEndOfFile ) {
				  bytearrayoutputstream.write(bytearrayBuffer,
						  intStart,intBytesRead);
			}//end while
			bytearrayoutputstream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		String stringReturn = bytearrayoutputstream.toString(); 
		return stringReturn;
	
	}// end
	
	private String getByteArrayOutputStream(InputStream inputstream, byte byteEndOfFile) {
		ByteArrayOutputStream bytearrayoutputstream = 
			new ByteArrayOutputStream();
		byte bytearrayBuffer[] = new byte[1000];
		int intBytesRead;
		int intStart = 0;
		int intStringID = 0;
		String[] stringarrayBuffer = new String[1000];
		try {
			while( (intBytesRead = inputstream.read(bytearrayBuffer)) 
					!= byteEndOfFile ) {
				  bytearrayoutputstream.write(bytearrayBuffer,
						  intStart,intBytesRead);
			}//end while

			int intLength = 1;
			int intStringBuffer = 0;
			for (int intBegin = 0; intBegin < bytearrayBuffer.length; intBegin++){
				//decode UTF-8 bytes bullshit
				if (intBegin == 0) {
					stringarrayBuffer[intStringBuffer] = new String( bytearrayBuffer, intBegin, intLength, "UTF-8" ); 
					intStringBuffer++;
				}
				if (intBegin == 1){
					
					stringarrayBuffer[intStringBuffer] = new String( bytearrayBuffer, intBegin, intLength = 2, "UTF-8" );
					intStringBuffer++;
				}
				if (intBegin == 13){
					stringarrayBuffer[intStringBuffer] = new String( bytearrayBuffer, intBegin, intLength = 4, "UTF-8" );
					intStringBuffer++;
				}
				if (intBegin == 18){
					Float floatBid = convertByteArrayToFloat (bytearrayBuffer, intBegin);
					
					stringarrayBuffer[intStringBuffer] = new String( bytearrayBuffer, intBegin, intLength = 4, "UTF-8" );
					intStringBuffer++;
				}
			}
			bytearrayoutputstream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		String stringReturn = bytearrayoutputstream.toString(); 
		//new DataInputStreamHandler(bytearrayoutputstream.);
		//String stringReturn = bytearrayBuffer.toString(); 
		return stringReturn;
	
	}

	public Float convertByteArrayToFloat (byte[] arr, int start) {
		int i = 0;
		int len = 4;
		int cnt = 0;
		byte[] tmp = new byte[len];
		for (i = start; i < (start + len); i++) {
			tmp[cnt] = arr[i];
			cnt++;
		}
		int accum = 0;
		i = 0;
		for ( int shiftBy = 0; shiftBy < 32; shiftBy += 8 ) {
			accum |= ( (long)( tmp[i] & 0xff ) ) << shiftBy;
			i++;
		}
		return Float.intBitsToFloat(accum);
	}


	public void setDataLogin(DataLogin dataLogin) {
			this.dataLogin = dataLogin; 
	}
	
	public void setDataStreamerInfo(DataStreamerInfo dataStreamerInfo) {
		this.dataStreamerInfo = dataStreamerInfo;
	}


}//end AmeritradeSecuritiesService class
